﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class mainForm : Form
    {
        private const int WM_DEVICECHANGE = 0x0219;
        private const int DBT_DEVICEARRIVAL = 0x8000;
        private const int DBT_DEVICEREMOVECOMPLETE = 0x8004;

        private const int BSF_NOHANG = 0x00000008;
        private const int BSF_POSTMESSAGE = 0x00000010;

        //[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_DEVICECHANGE:
                    if ((int)m.WParam == DBT_DEVICEARRIVAL)
                    {
                        recalcDriveAvailabilityOnTab(drivesPanel.SelectedTab); 
                    }
                    break;
            }
            base.WndProc(ref m);
        }

        public mainForm()
        {
            InitializeComponent();
            this.dropselectionlabel.Select();

            MenuItem trayMenuItemRestore = new MenuItem("Restore Window");
            MenuItem trayMenuItemExit = new MenuItem("Dismount All And Exit");

            trayMenuItems = new MenuItem[] { trayMenuItemRestore, trayMenuItemExit };

            trayMenu = new ContextMenu(trayMenuItems);
            trayNotifyIcon.ContextMenu = trayMenu;

            this.Resize += new System.EventHandler(this.Form1_SizeChanged);
            trayMenuItemRestore.Click += new System.EventHandler(this.trayMenuItemRestore_Click);
            trayMenuItemExit.Click += new System.EventHandler(this.trayMenuItemExit_Click);

            trayNotifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.mynotifyicon_MouseDoubleClick);

            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);

            trayNotifyIcon.Visible = false;

            vcryDrives = new ArrayList();


            drivesPanel = new MultiPagedPanel(this);
            drivesPanel.Location = new Point(0, 0);
            drivesPanel.Size = new Size(384, 256);
            addTabPage('E');
            addTabPage('J');
            addTabPage('O');
            addTabPage('T');
            this.Controls.Add(drivesPanel);
            drivesPanel.SelectedIndex = 0;
            recalcDriveAvailabilityOnTab(drivesPanel.SelectedTab);            
        }

        private void addTabPage(char startingletter)
        {
            TabPage tabPage = new System.Windows.Forms.TabPage();
            DriveButton db1 = new DriveButton(Convert.ToChar((((int)(startingletter)) + 0)).ToString() + ":\\");
            db1.Location = new Point(0, 0);
            DriveButton db2 = new DriveButton(Convert.ToChar((((int)(startingletter)) + 1)).ToString() + ":\\");
            db2.Location = new Point(128, 0);
            DriveButton db3 = new DriveButton(Convert.ToChar((((int)(startingletter)) + 2)).ToString() + ":\\");
            db3.Location = new Point(256, 0);
            DriveButton db4 = new DriveButton(Convert.ToChar((((int)(startingletter)) + 3)).ToString() + ":\\");
            db4.Location = new Point(0, 128);
            DriveButton db5 = new DriveButton(Convert.ToChar((((int)(startingletter)) + 4)).ToString() + ":\\");
            db5.Location = new Point(128, 128);

            db1.Click += new System.EventHandler(this.driveButton_Click);
            db2.Click += new System.EventHandler(this.driveButton_Click);
            db3.Click += new System.EventHandler(this.driveButton_Click);
            db4.Click += new System.EventHandler(this.driveButton_Click);
            db5.Click += new System.EventHandler(this.driveButton_Click);

            tabPage.Controls.Add(db1);
            tabPage.Controls.Add(db2);
            tabPage.Controls.Add(db3);
            tabPage.Controls.Add(db4);
            tabPage.Controls.Add(db5);

            drivesPanel.Controls.Add(tabPage);
        }

        private void recalcDriveAvailabilityOnTab(TabPage t)
        {
            DriveInfo[] allDrives = DriveInfo.GetDrives();
            int n = allDrives.GetLength(0);

            foreach (DriveButton b in t.Controls)
            {
                b.Enabled = true;

                for (int i = 1; i < n; i++)
                {
                    if (allDrives[i].Name == b.Text)
                    {
                        if (b.IsDriveAttached == false)
                        {
                            b.Enabled = false;
                            break;
                        }
                    }
                }
            }
        }

        private void driveButton_Click(object sender, EventArgs e)
        {
            DriveButton b = sender as DriveButton;
            char l = b.Text[0];

            if (b.IsDriveAttached == false)
            {
                OpenFileDialog openFileDialog1 = new OpenFileDialog();
                openFileDialog1.Title = "Choose a file to connect as CryDisk";
                openFileDialog1.Filter = "CryptDisk Volume files (*.cdv)|*.cdv|All files (*.*)|*.*";
                openFileDialog1.FilterIndex = 1;
                openFileDialog1.Multiselect = false;

                StringBuilder sTest;
                if (openFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    sTest = new StringBuilder(openFileDialog1.FileName);
                    byte[] header = new byte[16];

                    try
                    {
                        using (FileStream fs = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read))
                        {
                            BinaryReader br = new BinaryReader(fs);
                            header = br.ReadBytes(16);
                        }
                    }
                    catch (IOException ioex)
                    {
                        MessageBox.Show("File Is Already In Use","System.Io.Exception");
                        this.dropselectionlabel.Select();
                        return;
                    }

                    enterPwdForm pwf = new enterPwdForm(header, openFileDialog1.FileName);
                    if (pwf.ShowDialog() == DialogResult.No) 
                    {
                        this.dropselectionlabel.Select();
                        return;
                    }
                }
                else
                {
                    this.dropselectionlabel.Select();
                    return;
                }

                try
                {
                    int reslt = Win32Imported.vcrydMount(sTest, Convert.ToByte(l));

                    if (reslt == 0)
                    {
                        DriveInfo d = new DriveInfo(b.Text);
                        if (!d.IsReady)
                            formatDrive(l);

                        vcryDrives.Add(b.Text);
                        b.AttachVD(sTest.ToString());
                        notifyAboutDiskAdded(l);
                    }
                    else
                    {
                        MessageBox.Show("vcrydMount returned " + reslt.ToString(), "vcrydMount non null return");
                    }
                }
                catch (DllNotFoundException dex)
                {
                    MessageBox.Show("Cannot find vcrydcaller.dll", "Dll not found");
                }
            }
            else
            {
                try
                {
                    long reslt = Win32Imported.vcrydDismount(Convert.ToByte(l));

                    if (reslt == 0)
                    {
                        vcryDrives.Remove(b.Text.Substring(0, 3));
                        b.DetachVD();
                        notifyAboutDiskRemoved(l);
                    }
                    else
                    {
                        MessageBox.Show("vcrydDismount returned " + reslt.ToString(), "vcrydDismount non null return");
                    }
                }
                catch (DllNotFoundException dex)
                {
                    MessageBox.Show("Cannot find vcrydcaller.dll", "Dll not found");
                }
            }

            this.dropselectionlabel.Select();
        }


        private void exitbutton_Click(object sender, EventArgs e)
        {
            unmountall();
            if (vcryDrives.Count == 0)
            {
                this.Close();
            }
            else
            {
                MessageBox.Show(String.Format("Cannot unmount {0} disks", vcryDrives.Count));
                this.dropselectionlabel.Select();        
            }
        }
        

        private void trayMenuItemRestore_Click(object sender, EventArgs e)
        {
            restoreWindowAndRemoveTrayIcon();
        }

        private void trayMenuItemExit_Click(object sender, EventArgs e)
        {
            unmountall();
            if (vcryDrives.Count == 0)
            {
                this.Close();
            }
            else
                MessageBox.Show(String.Format("Cannot unmount {0} disks", vcryDrives.Count));
        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            if (FormWindowState.Normal == this.WindowState)
            {
                trayNotifyIcon.Visible = false;
            }
        }

        private void mynotifyicon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            restoreWindowAndRemoveTrayIcon();
        }

        private void restoreWindowAndRemoveTrayIcon()
        {
            this.Show();
            this.WindowState = FormWindowState.Normal;
            trayNotifyIcon.Visible = false;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (vcryDrives.Count!=0)
            {
                e.Cancel = true;
                trayNotifyIcon.Visible = true;
                this.Hide();
            }
        }

        private void newfileButton_Click(object sender, EventArgs e)
        {
            CreateDriveForm f = new CreateDriveForm();
            f.ShowDialog();
        }

        private void unmountall()
        {
            try
            {
                foreach (TabPage t in drivesPanel.TabPages)
                {
                    foreach (DriveButton b in t.Controls)
                    {
                        if (b.IsDriveAttached == true)
                        {
                            char l = b.Text[0];
                            int reslt = Win32Imported.vcrydDismount(Convert.ToByte(l));

                            if (reslt == 0)
                            {
                                vcryDrives.Remove(b.Text.Substring(0, 3));
                                b.DetachVD();
                                notifyAboutDiskRemoved(l);
                            }
                        }
                    }
                }
            }
            catch (DllNotFoundException dex)
            {
                MessageBox.Show("Cannot find vcrydcaller.dll", "Dll not found");
            }
        }

        private void mintotrayButton_Click(object sender, EventArgs e)
        {
            trayNotifyIcon.Visible = true;
            this.Hide();
        }

        private void nButton_Click(object sender, EventArgs e)
        {
            if (drivesPanel.SelectedIndex < 5)
            {
                drivesPanel.SelectedIndex++;
                recalcDriveAvailabilityOnTab(drivesPanel.SelectedTab);
            }

            this.dropselectionlabel.Select();
        }

        private void pButton_Click(object sender, EventArgs e)
        {
            if (drivesPanel.SelectedIndex > 0)
            {
                drivesPanel.SelectedIndex--;
                recalcDriveAvailabilityOnTab(drivesPanel.SelectedTab);
            }

            this.dropselectionlabel.Select();
        }

        private void notifyAboutDiskAdded(char volumeToRefresh)
        {
            DEV_BROADCAST_VOLUME broadcast = new DEV_BROADCAST_VOLUME();
            broadcast.dbcv_devicetype = DeviceType.DBT_DEVTYP_VOLUME;
            broadcast.dbcv_reserved = 0;
            broadcast.dbcv_unitmask = (uint)1 << (volumeToRefresh - 'A');
            broadcast.dbcv_flags = VolumeChangeFlags.DBTF_NET;

            int size = System.Runtime.InteropServices.Marshal.SizeOf(broadcast);
            broadcast.dbcv_size = (uint)size;

            IntPtr buffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
            System.Runtime.InteropServices.Marshal.StructureToPtr(broadcast, buffer, true);

            int recipients = 0;
            Win32Imported.BroadcastSystemMessage(BSF_NOHANG | BSF_POSTMESSAGE,
            ref recipients, WM_DEVICECHANGE, DBT_DEVICEARRIVAL, buffer);

            System.Runtime.InteropServices.Marshal.FreeHGlobal(buffer);
        }

        private void notifyAboutDiskRemoved(char volumeToRefresh)
        {
            DEV_BROADCAST_VOLUME broadcast = new DEV_BROADCAST_VOLUME();
            broadcast.dbcv_devicetype = DeviceType.DBT_DEVTYP_VOLUME;
            broadcast.dbcv_reserved = 0;
            broadcast.dbcv_unitmask = (uint)1 << (volumeToRefresh - 'A');
            broadcast.dbcv_flags = VolumeChangeFlags.DBTF_NET;

            int size = System.Runtime.InteropServices.Marshal.SizeOf(broadcast);
            broadcast.dbcv_size = (uint)size;

            IntPtr buffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
            System.Runtime.InteropServices.Marshal.StructureToPtr(broadcast, buffer, true);

            int recipients = 0;
            Win32Imported.BroadcastSystemMessage(BSF_NOHANG | BSF_POSTMESSAGE,
            ref recipients, WM_DEVICECHANGE, DBT_DEVICEREMOVECOMPLETE, buffer);

            System.Runtime.InteropServices.Marshal.FreeHGlobal(buffer);
        }

        private void formatDrive(char volume)
        {
            uint result = Win32Imported.SHFormatDrive(IntPtr.Zero, (uint)volume - 'A', (uint)SHFormatFlags.SHFMT_ID_DEFAULT, 0);
            if (result == (uint)SHFormatFlags.SHFMT_ERROR)
                MessageBox.Show("Error occured");
            if (result == (uint)SHFormatFlags.SHFMT_NOFORMAT)
                MessageBox.Show("The drive cannot be formatted");
            if (result == (uint)SHFormatFlags.SHFMT_CANCEL)
                MessageBox.Show("Format was cancelled");
        }
    }
}
